package br.com.linkin.mi.utils.data;


import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Locale;

/**
 *
 * @author Daniel Mendonca
 */
/**
 * Classe utilitária para manipulação de datas.
 */
public class DateUtil {

    private DateUtil() {
    }
    private static final Locale DEFAULT_LOCALE = new Locale("pt", "BR");
    private static final SimpleDateFormat FORMATTER_TIME_SHORT = new SimpleDateFormat("HH:mm", DEFAULT_LOCALE);
    private static final SimpleDateFormat FORMATTER_TIME = new SimpleDateFormat("HH:mm:ss", DEFAULT_LOCALE);
    private static final SimpleDateFormat FORMATTER_DATE = new SimpleDateFormat("dd/MM/yyyy", DEFAULT_LOCALE);
    private static final SimpleDateFormat FORMATTER_DATE_TIME = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss", DEFAULT_LOCALE);
    private static final SimpleDateFormat FORMATTER_DATE_TIME_yyyyMMddHHmmss = new SimpleDateFormat("yyyyMMddHHmmss", DEFAULT_LOCALE);
    private static final SimpleDateFormat FORMATTER_DATE_YYYYMMDD = new SimpleDateFormat("yyyyMMdd", DEFAULT_LOCALE);
    private static final SimpleDateFormat FORMATTER_DATE_DDMMYYYY = new SimpleDateFormat("ddMMyyyy", DEFAULT_LOCALE);

    static {
        FORMATTER_TIME.setLenient(false);
        FORMATTER_DATE.setLenient(false);
        FORMATTER_DATE_TIME.setLenient(false);
        FORMATTER_DATE_TIME_yyyyMMddHHmmss.setLenient(false);
        FORMATTER_DATE_YYYYMMDD.setLenient(false);
        FORMATTER_DATE_DDMMYYYY.setLenient(false);
    }

    /**
     * Formata a hora (HH:mm:ss). Retorna null se o argumento passado for null.
     * 
     * @param dt objeto representando data e/ou hora.
     * 
     * @return hora formatada.
     */
    public static String timeFormat(Date dt) {
        return format(dt, FORMATTER_TIME);
    }

    /**
     * Formata a hora (HH:mm). Retorna null se o argumento passado for null.
     * 
     * @param dt objeto representando data e/ou hora.
     * 
     * @return hora formatada.
     */
    public static String simpleTimeFormat(Date dt) {
        return format(dt, FORMATTER_TIME_SHORT);
    }

    /**
     * Formata a data (dd/MM/yyyy). Retorna null se o argumento passado for null.
     * 
     * @param dt objeto representando data e/ou hora.
     * 
     * @return data formatada.
     */
    public static String dateFormat(Date dt) {
        return format(dt, FORMATTER_DATE);
    }

    /**
     * Formata data e hora (dd/MM/yyyy HH:mm:ss). Retorna null se o argumento 
     * passado for null.
     * 
     * @param dt objeto representando data e/ou hora.
     * 
     * @return data e hora formatada.
     */
    public static String dateTimeFormat(Date dt) {
        return format(dt, FORMATTER_DATE_TIME);
    }

    /**
     * Formata data e hora (yyyyMMddHHmmss). Retorna null se o argumento passado 
     * for null.
     * 
     * @param dt objeto representando data e/ou hora.
     * 
     * @return data e hora formatada.
     */
    public static String dateTimeFormatYMDHMS(Date dt) {
        return format(dt, FORMATTER_DATE_TIME_yyyyMMddHHmmss);
    }

    /**
     * Formata a data (yyyy-MM-dd). Retorna null se o argumento passado for null.
     * 
     * @param dt objeto representando data e/ou hora.
     * 
     * @return data formatada.
     */
    public static String dateFormatYYYYMMDD(Date dt) {
        return format(dt, FORMATTER_DATE_YYYYMMDD);
    }

    /**
     * Formata a data (ddMMyyyy). Retorna null se o argumento passado for null.
     * 
     * @param dt objeto representando data e/ou hora.
     * 
     * @return data formatada.
     */
    public static String dateFormatDDMMYYYY(Date dt) {
        return format(dt, FORMATTER_DATE_DDMMYYYY);
    }

    /**
     * Formata a data na máscara e localização informados. Retorna null se o 
     * argumento passado for null.
     * 
     * @param dt objeto representando data e/ou hora.
     * @param mask máscara para formatação.
     * 
     * @return data formatada.
     */
    public static String format(Date dt, String mask) {
        return format(dt, mask, DEFAULT_LOCALE);
    }

    /**
     * Formata a data na máscara e localização informados. Retorna null se o 
     * argumento passado for null.
     * 
     * @param dt objeto representando data e/ou hora.
     * @param mask máscara para formatação.
     * @param local Locale (localização) para a formatação.
     * 
     * @return data formatada.
     */
    public static String format(Date dt, String mask, Locale local) {
        DateFormat df = new SimpleDateFormat(mask, local);
        df.setLenient(false);
        return format(dt, df);
    }

    /**
     * Formata a data. Retorna null se o argumento passado for null.
     * 
     * @param dt objeto representando data e/ou hora.
     * @param df formatador da data.
     * 
     * @return data formatada.
     */
    private static String format(Date dt, DateFormat df) {
        return (dt == null) ? null : df.format(dt);
    }

    /**
     * Verifica se a data e hora são logicamente válidas, ou seja, se 
     * representam uma data e uma hora real do calendário gregoriano (formato 
     * brasileiro), além de verificar se a data é menor ou igual à data máxima
     * do sistema do SSV (31/12/2699).
     * 
     * @param dateHour Data e hora a ser validada, no formato "dd/MM/yyyy HH:mm:ss".
     * 
     * @return true se for válida ou false caso contrário.
     */
    public static boolean isValidDateHour(String dateHour) {
        if (dateHour == null || dateHour.length() != 19) {
            return false;
        }
        return Data.isValid(dateHour.substring(0, 10))
                && Time.isValid(dateHour.substring(11));
    }

    /**
     * Verifica se a data é logicamente válida, ou seja, se representa uma data
     * real do calendário gregoriano (formato brasileiro), além de verificar se 
     * a data é menor ou igual à data máxima do sistema do SSV (31/12/2699).
     * 
     * @param date Data a ser validada, no formato "dd/MM/yyyy".
     * 
     * @return true se for válida ou false caso contrário.
     */
    public static boolean isValidDate(String date) {
        return Data.isValid(date);
    }

    /**
     * Verifica se a hora é logicamente válida, ou seja, se representa uma hora
     * real no formato brasileiro.
     * 
     * @param hr Time a ser validada, no formato "HH:mm:ss".
     * 
     * @return true se for válida ou false caso contrário.
     */
    public static boolean isValidHour(String hr) {
        return Time.isValid(hr);
    }

    /**
     * Faz o parsing de uma data. Retorna null se o argumento passado for null.
     * 
     * @param dt String representando uma data no formato "dd/MM/yyyy".
     * 
     * @return Data.
     * 
     * @throws ParseException Em caso de erro de parsing ou data inválida.
     */
    public static Date parseDate(String dt) throws ParseException {
        return parse(dt, FORMATTER_DATE);
    }

    /**
     * Faz o parsing de uma data e hora. Retorna null se o argumento passado
     * for null.
     * 
     * @param dt String representando uma data no formato "dd/MM/yyyy HH:mm:ss".
     * 
     * @return Data.
     * 
     * @throws ParseException Em caso de erro de parsing ou data/hora inválida.
     */
    public static Date parseDateTime(String dt) throws ParseException {
        return parse(dt, FORMATTER_DATE_TIME);
    }

    /**
     * Faz o parsing de uma data e hora. Retorna null se o argumento passado 
     * for null.
     * 
     * @param dt String representando uma data no formato "yyyyMMddHHmmss".
     * 
     * @return Data.
     * 
     * @throws ParseException Em caso de erro de parsing ou data/hora inválida.
     */
    public static Date parseDateTimeYMDHMS(String dt) throws ParseException {
        return parse(dt, FORMATTER_DATE_TIME_yyyyMMddHHmmss);
    }

    /**
     * Faz o parsing de uma data. Retorna null se o argumento passado for null.
     * 
     * @param dt String representando uma data no formato "yyyyMMdd".
     * 
     * @return Data.
     * 
     * @throws ParseException Em caso de erro de parsing ou data inválida.
     */
    public static Date parseDateYYYYMMDD(String dt) throws ParseException {
        return parse(dt, FORMATTER_DATE_YYYYMMDD);
    }

    /**
     * Faz o parsing de uma data. Retorna null se o argumento passado for null.
     * 
     * @param dt String representando uma data no formato "ddMMyyyy".
     * 
     * @return Data.
     * 
     * @throws ParseException Em caso de erro de parsing ou data inválida.
     */
    public static Date parseDateDDMMYYYY(String dt) throws ParseException {
        return parse(dt, FORMATTER_DATE_DDMMYYYY);
    }

    /**
     * Faz o parsing da String que representa a data informada, utilizando a 
     * máscara (formato) passada como argumento. Se a data for null, então 
     * retorna null.
     * 
     * @param dt String representando uma data.
     * @param mask máscara do formato da String da data.
     * 
     * @return data.
     * 
     * @throws ParseException em caso de erro de parsing.
     */
    public static Date parse(String dt, String mask) throws ParseException {
        return parse(dt, mask, DEFAULT_LOCALE);
    }

    /**
     * Faz o parsing da String que representa a data informada, utilizando a 
     * máscara (formato) e localização passados como argumentos. Se a data for 
     * null, então retorna null.
     * 
     * @param dt String representando uma data.
     * @param mask máscara do formato da String da data.
     * @param local Locale (localização) do formato da data.
     * 
     * @return data.
     * 
     * @throws ParseException em caso de erro de parsing.
     */
    public static Date parse(String dt, String mask, Locale local) throws ParseException {
        DateFormat df = new SimpleDateFormat(mask, local);
        df.setLenient(false);
        return parse(dt, df);
    }

    /**
     * Faz o parsing da String que representa a data informada, utilizando o 
     * formatador passado. Se a data for null, então retorna null.
     * 
     * @param dt String representando uma data.
     * @param df DateFormat a ser usado para o parsing da data.
     * 
     * @return Data.
     * 
     * @throws ParseException em caso de erro de parsing.
     */
    public static Date parse(String dt, DateFormat df) throws ParseException {
        if (dt == null) {
            return null;
        }
        Date data = df.parse(dt);
        // verifica se a data "parseada" é igual à data informada no argumento
        // isso valida se a data é válida, ou seja, se não foi passada uma data
        // existente, com o valor "35/01/2008", por exemplo
        if (df.format(data).equals(dt)) {
            return data;
        } else {
            throw new ParseException("Data inválida", 0);
        }
    }

    /**
     * Retorna o day da semana da data informada.
     * 
     * @param dt Data.
     * 
     * @return Dia da semana.
     */
    public static Integer getDiaDaSemana(Date dt) {
        if (dt == null) {
            return null;
        }
        Calendar calendar = new GregorianCalendar();
        calendar.setLenient(false);
        calendar.setTime(dt);
        return calendar.get(Calendar.DAY_OF_WEEK);
    }

    /**
     * Retorna o day do mês da data informada.
     * 
     * @param dt Data.
     * 
     * @return Dia do mês.
     */
    public static Integer getDayOfMonth(Date dt) {
        if (dt == null) {
            return null;
        }
        Calendar calendar = new GregorianCalendar();
        calendar.setLenient(false);
        calendar.setTime(dt);
        return calendar.get(Calendar.DAY_OF_MONTH);
    }

    /**
     * Retorna o day do ano da data informada.
     * 
     * @param dt Data.
     * 
     * @return Dia do ano.
     */
    public static Integer getDayOfYear(Date dt) {
        if (dt == null) {
            return null;
        }
        Calendar calendar = new GregorianCalendar();
        calendar.setLenient(false);
        calendar.setTime(dt);
        return calendar.get(Calendar.DAY_OF_YEAR);
    }

    /**
     * Retorna o último day do mês da data informada.
     * 
     * @param dt Data.
     * 
     * @return Último day do mês.
     */
    public static Integer getLastDayOfMonth(Date dt) {
        if (dt == null) {
            return null;
        }
        Calendar calendar = new GregorianCalendar();
        calendar.setLenient(false);
        calendar.setTime(dt);
        return calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
    }

    /**
     * Retorna a diferença em dias entra duas datas (posterior e anterior).
     * 
     * @param depois Data mais tarde.
     * @param antes Data mais cedo.
     * 
     * @return Número de dias da diferença entra as datas.
     */
    public static long getDaysDiference(Date depois, Date antes) {
        long millis = depois.getTime() - antes.getTime();
        return millis / (1000 * 3600 * 24);
    }

    /**
     * Retorna o mês da data informada.
     * 
     * @param dt Data.
     * 
     * @return Mês.
     */
    public static Integer getMonth(Date dt) {
        if (dt == null) {
            return null;
        }
        Calendar calendario = new GregorianCalendar();
        calendario.setLenient(false);
        calendario.setTime(dt);
        return calendario.get(Calendar.MONTH) + 1;
    }

    /**
     * Retorna o ano da data informada.
     * 
     * @param dt Data.
     * 
     * @return Ano.
     */
    public static Integer getYear(Date dt) {
        if (dt == null) {
            return null;
        }
        Calendar calendar = new GregorianCalendar();
        calendar.setLenient(false);
        calendar.setTime(dt);
        return calendar.get(Calendar.YEAR);
    }

    /**
     * Retorna a semana do mês da data informada.
     * 
     * @param dt Data.
     * 
     * @return Número da semana no mês.
     */
    public static Integer getWeekMonth(Date dt) {
        if (dt == null) {
            return null;
        }
        Calendar calendar = new GregorianCalendar();
        calendar.setLenient(false);
        calendar.setTime(dt);
        return calendar.get(Calendar.WEEK_OF_MONTH);
    }

    /**
     * Retorna a semana do ano da data informada.
     * 
     * @param dt Data.
     * 
     * @return Número da semana no ano.
     */
    public static Integer getWeekYear(Date dt) {
        if (dt == null) {
            return null;
        }
        Calendar calendar = new GregorianCalendar();
        calendar.setLenient(false);
        calendar.setTime(dt);
        return calendar.get(Calendar.WEEK_OF_YEAR);
    }

    /**
     * Retorna a data em extenso. Exemplo: "Sábado, 03 de Novembro de 2007".
     * 
     * @param dt Data.
     * 
     * @return Data em extenso.
     */
    public static String getExtensiveDate(Date dt) {
        return getDayNameOfWeek(dt) + ", " + getDayOfMonth(dt) + " de "
                + getMonthName(dt) + " de " + getYear(dt);
    }

    /**
     * Retorna a data em extenso. Exemplo: "Sab, 03 de Nov de 2007".
     * 
     * @param dt Data.
     * 
     * @return Data em extenso.
     */
    public static String getDataExtensoAbreviado(Date dt) {
        return getShortDayNameOfWeek(dt) + ", " + getDayOfMonth(dt) + " de "
                + getAbbreviatedMonthName(dt) + " de " + getYear(dt);
    }

    /**
     * Retorna o nome do day da semana (Domingo, Segunda-feira, etc).
     * 
     * @param dt Data.
     * 
     * @return Nome do day da semana.
     */
    public static String getDayNameOfWeek(Date dt) {
        int day = getDiaDaSemana(dt);
        switch (day) {
            case 1:
                return "Domingo";
            case 2:
                return "Segunda-feira";
            case 3:
                return "Terça-feira";
            case 4:
                return "Quarta-feira";
            case 5:
                return "Quinta-feira";
            case 6:
                return "Sexta-feira";
            case 7:
                return "Sábado";
        }
        return null;
    }

    /**
     * Retorna o nome do day da semana abreviado (Dom, Seg, Ter, etc).
     * 
     * @param dt Data.
     * 
     * @return Nome do day da semana abreviado.
     */
    public static String getShortDayNameOfWeek(Date dt) {
        int day = getDiaDaSemana(dt);
        switch (day) {
            case 1:
                return "Dom";
            case 2:
                return "Seg";
            case 3:
                return "Ter";
            case 4:
                return "Qua";
            case 5:
                return "Qui";
            case 6:
                return "Sex";
            case 7:
                return "Sab";
        }
        return null;
    }

    /**
     * Retorna o nome do mês (Janeiro, Fevereiro, etc).
     * 
     * @param dt Data.
     * 
     * @return Nome do mês.
     */
    public static String getMonthName(Date dt) {
        int month = getMonth(dt);
        switch (month) {
            case 1:
                return "Janeiro";
            case 2:
                return "Fevereiro";
            case 3:
                return "Março";
            case 4:
                return "Abril";
            case 5:
                return "Maio";
            case 6:
                return "Junho";
            case 7:
                return "Julho";
            case 8:
                return "Agosto";
            case 9:
                return "Setembro";
            case 10:
                return "Outubro";
            case 11:
                return "Novembro";
            case 12:
                return "Dezembro";
        }
        return null;
    }

    /**
     * Retorna o nome do mês abreviado (Jan, Fev, Mar, etc).
     * 
     * @param dt Data.
     * 
     * @return Nome do mês abreviado.
     */
    public static String getAbbreviatedMonthName(Date dt) {
        int month = getMonth(dt);
        switch (month) {
            case 1:
                return "Jan";
            case 2:
                return "Fev";
            case 3:
                return "Mar";
            case 4:
                return "Abr";
            case 5:
                return "Mai";
            case 6:
                return "Jun";
            case 7:
                return "Jul";
            case 8:
                return "Ago";
            case 9:
                return "Set";
            case 10:
                return "Out";
            case 11:
                return "Nov";
            case 12:
                return "Dez";
        }
        return null;
    }

    /**
     * Calcula uma nova data, baseado na data informada, adicionando as 
     * informações adicionais passadas como argumento.
     * <br>
     * Exemplo: Para calcular uma data 10 (dez) dias depois de hoje, faca:
     * <br>
     * <code>Date nova = DateUtil.newDate(new Date(), 0, 0, 10);</code>
     * 
     * @param dt Data base para cálculo.
     * @param anoShift Número de anos para calcular nova data.
     * @param mesShift Número de meses para calcular nova data.
     * @param diaShift Número de dias para calcular nova data.
     * 
     * @return Data calculada.
     */
    public static Date newDate(Date dt, int anoShift, int mesShift, int diaShift) {
        return newDate(dt, anoShift, mesShift, diaShift, 0, 0, 0);
    }

    /**
     * Calcula uma nova data, baseado na data informada, adicionando as 
     * informações adicionais passadas como argumento.
     * <br>
     * Exemplo: Para calcular uma data 10 (dez) dias depois de hoje, faca:
     * <br>
     * <code>Date nova = DateUtil.newDate(new Date(), 0, 0, 10, 0, 0, 0);</code>
     * 
     * @param dt Data base para cálculo.
     * @param anoShift Número de anos para calcular nova data.
     * @param mesShift Número de meses para calcular nova data.
     * @param diaShift Número de dias para calcular nova data.
     * @param horaShift Número de horas para calcular nova data.
     * @param minutoShift Número de minutos para calcular nova data.
     * @param segundoShift Número de segundos para calcular nova data.
     * 
     * @return Data calculada.
     */
    public static Date newDate(Date dt, int anoShift, int mesShift,
            int diaShift, int horaShift,
            int minutoShift, int segundoShift) {
        return newDate(dt, anoShift, mesShift, diaShift, horaShift, minutoShift, segundoShift, 0);
    }

    /**
     * Calcula uma nova data, baseado na data informada, adicionando as 
     * informações adicionais passadas como argumento.
     * <br>
     * Exemplo: Para calcular uma data 10 (dez) dias depois de hoje, faca:
     * <br>
     * <code>Date nova = DateUtil.newDate(new Date(), 0, 0, 10, 0, 0, 0, 0);</code>
     * 
     * @param dt Data base para cálculo.
     * @param anoShift Número de anos para calcular nova data.
     * @param mesShift Número de meses para calcular nova data.
     * @param diaShift Número de dias para calcular nova data.
     * @param horaShift Número de horas para calcular nova data.
     * @param minutoShift Número de minutos para calcular nova data.
     * @param segundoShift Número de segundos para calcular nova data.
     * @param miliShift Número de milisegundos para calcular nova data.
     * 
     * @return Data calculada.
     */
    public static Date newDate(Date dt, int anoShift, int mesShift,
            int diaShift, int horaShift,
            int minutoShift, int segundoShift,
            int miliShift) {
        if (dt == null) {
            return null;
        }
        Calendar calendar = new GregorianCalendar();
        calendar.setLenient(false);
        calendar.setTime(dt);
        calendar.add(Calendar.YEAR, anoShift);
        calendar.add(Calendar.MONTH, mesShift);
        calendar.add(Calendar.DAY_OF_YEAR, diaShift);
        calendar.add(Calendar.HOUR, horaShift);
        calendar.add(Calendar.MINUTE, minutoShift);
        calendar.add(Calendar.SECOND, segundoShift);
        calendar.add(Calendar.MILLISECOND, miliShift);
        return calendar.getTime();
    }

    /**
     * Verifica se a primeira data está entre as duas outras datas informadas,
     * inclusive, ou seja, se D1 é maior ou igual a D2 e menor ou igual a D3.
     * Data D2 deve ser menor ou igual data D3. Este método considera a data 
     * como um todo, ou seja o seu timestamp (data e hora).
     * 
     * @param d1 data a ser verficada.
     * @param d2 data inicial.
     * @param d3 data final.
     * 
     * @return true se data D1 está entre D2 e D3, ou false caso contrário.
     */
    public static boolean isBetween(Date d1, Date d2, Date d3) {
        return (d1.compareTo(d2) >= 0 && d1.compareTo(d3) <= 0);
    }

    /**
     * Verifica se a primeira data está entre as duas outras datas informadas,
     * inclusive, ou seja, se D1 é maior ou igual a D2 e menor ou igual a D3.
     * Data D2 deve ser menor ou igual data D3. Este método considera a data 
     * apenas, ou seja, String no formato "dd/MM/yyyy".
     * 
     * @param d1 data a ser verficada.
     * @param d2 data inicial.
     * @param d3 data final.
     * 
     * @return true se data D1 está entre D2 e D3, ou false caso contrário.
     * 
     * @throws ParseException Em caso de erro de parsing das datas.
     */
    public static boolean isBetween(String d1, String d2, String d3) throws ParseException {
        return isBetween(FORMATTER_DATE.parse(d1), FORMATTER_DATE.parse(d2), FORMATTER_DATE.parse(d3));
    }

    /**
     * Truca a data/hora passada, retirando o horário e atribuindo para zero o 
     * valor. Exemplo: Para a data/hora "01/01/2008 12:10:00.0", truncar e 
     * transforma o valor para "01/01/2008 00:00:00.0", ou seja, ignora o 
     * horário.
     * 
     * @param dt data a ser truncada.
     * 
     * @return nova data.
     */
    public static Date dateTruncate(Date dt) {
        Calendar cal = Calendar.getInstance();
        cal.clear();
        cal.setTime(dt);
        cal.set(Calendar.AM_PM, Calendar.AM);
        cal.set(Calendar.HOUR, 0);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.SECOND, 0);
        cal.set(Calendar.MILLISECOND, 0);
        return cal.getTime();
    }

    /**
     * Retorna o 1° dia a partir de uma determinada data
     * @param dt
     * @return 
     */
    public static Date getFirstDay(Date dt) {

        Date date = null;
        try {
            date = parse("1/" + getMonth(dt) + "/" + getYear(dt), "d/M/yyyy");
        } catch (ParseException ex) {
            date = null;
        }

        return date;

    }

    /**
     * Retorna a data com o ultimo dia a partir de uma determinada data
     * @param dt
     * @return 
     */
    public static Date getLastDay(Date dt) {

        Date date = null;
        try {
            date = parse(getLastDayOfMonth(dt) + "/" + getMonth(dt) + "/" + getYear(dt), "d/M/yyyy");
        } catch (ParseException ex) {
            date = null;
        }

        return date;

    }

    /**
     * Obtem o intervalo em dias corridos entre duas datas 
     * @param d1 - Data de
     * @param d2 - Data Ate
     * @return 
     */
    public static Integer dateDiff(Date dt1, Date dt2) {

        Integer diff = 0;
        GregorianCalendar cal1 = new GregorianCalendar();
        GregorianCalendar cal2 = new GregorianCalendar();

        String calS1 = "";
        String calS2 = "";

        Date d1 = dateTruncate(dt1);
        Date d2 = dateTruncate(dt2);

        if (d1.after(d2)) {
            cal1.setTime(d2);
            cal2.setTime(d1);
        } else {
            cal1.setTime(d1);
            cal2.setTime(d2);
        }

        calS1 = dateFormat(cal1.getTime());
        calS2 = dateFormat(cal2.getTime());

        while (!calS2.equals(calS1)) {
            cal2.add(GregorianCalendar.DAY_OF_MONTH, -1);
            calS2 = dateFormat(cal2.getTime());
            diff++;
        }

        return diff;
    }

    /**
     * Obtem o intervalo em dias Uteis entre duas datas 
     * @param d1 - Data de
     * @param d2 - Data Ate
     * @return 
     */
    public static Integer dateDiffUtil(Date dt1, Date dt2) {

        Integer diff = 0;
        GregorianCalendar cal1 = new GregorianCalendar();
        GregorianCalendar cal2 = new GregorianCalendar();

        String calS1 = "";
        String calS2 = "";

        Date d1 = dateTruncate(dt1);
        Date d2 = dateTruncate(dt2);

        if (d1.after(d2)) {
            cal1.setTime(d2);
            cal2.setTime(d1);
        } else {
            cal1.setTime(d1);
            cal2.setTime(d2);
        }

        calS1 = dateFormat(cal1.getTime());
        calS2 = dateFormat(cal2.getTime());

        while (!calS2.equals(calS1)) {
            cal2.add(GregorianCalendar.DAY_OF_MONTH, -1);
            calS2 = dateFormat(cal2.getTime());
            if (cal2.get(GregorianCalendar.DAY_OF_WEEK) != GregorianCalendar.SUNDAY
                    && cal2.get(GregorianCalendar.DAY_OF_WEEK) != GregorianCalendar.SATURDAY) {
                diff++;
            }
        }

        return diff;
    }

    /**
     * Obtem o intervalo em dias corridos no ano 
     * @param year
     * @return 
     */
    public static Integer totalYearDays(Integer year) {
        Integer diff = 0;
        GregorianCalendar cal1 = new GregorianCalendar();
        GregorianCalendar cal2 = new GregorianCalendar();

        cal1.set(year, 0, 1);
        cal2.set(year, 11, 31);

        return dateDiff(cal1.getTime(), cal2.getTime());
    }

    /**
     * Obtem o intervalo em dias Uteis no ano
     * @param year
     * @return 
     */
    public static Integer totalYearDaysUtil(Integer year) {
        Integer diff = 0;
        GregorianCalendar cal1 = new GregorianCalendar();
        GregorianCalendar cal2 = new GregorianCalendar();

        cal1.set(year, 0, 1);
        cal2.set(year, 11, 31);

        return dateDiffUtil(cal1.getTime(), cal2.getTime());
    }

    /**
     * Obtém a lista de datas adicionando um mês a menor data informada
     * até chegar ao mês da maior data informada
     * @param date1
     * @param date2
     * @return List(Date)
     */
    public static List<Date> getListDatesByMonth(Date date1, Date date2) {

        List<Date> listDates = new ArrayList<Date>();

        date1 = DateUtil.dateTruncate(date1);
        date2 = DateUtil.dateTruncate(date2);

        GregorianCalendar cl1 = new GregorianCalendar();
        GregorianCalendar cl2 = new GregorianCalendar();

        if (date1.after(date2)) {
            cl1.setTime(date2);
            cl2.setTime(date1);
        } else {
            cl1.setTime(date1);
            cl2.setTime(date2);
        }
        
        while (cl1.compareTo(cl2) <= 0 ) {
            listDates.add(cl1.getTime());
            cl1.add(Calendar.MONTH, 1);
        }
        return listDates;
    }
    
    public static Date parseTime(String time) throws ParseException{
        return parse(time, FORMATTER_TIME);
    }
}
